/*
 * Matriz.cpp
 *
 *  Created on: 29/11/2010
 *      Author: santiago
 */

#include "Matriz.h"

Matriz::Matriz() {
}

Matriz::Matriz(int h1, int h2, int tamanio) {
	this->tamanioMatriz = tamanio;

	std::vector<int> fila;
	for (int i = 0; i< this->tamanioMatriz; i++){
		for(int j =0; j< this->tamanioMatriz; j++){
			if (j< i){
				fila.push_back(0);
			}
			else{
				int res = ((h1*h2)*(i+j+1))%256;
				fila.push_back(res);
			}
		}
		this->matriz.push_back(fila);
		fila.clear();
	}

	//ajustamos la primer posicion de la matriz como piden
	long aux = this->matriz[1][1];
	for(int k=2; k< this->tamanioMatriz; k++){
		aux = (int)(aux * this->matriz[k][k]) % 256;
	}
	//aux = (1/aux) % 256;
	aux = this->InversaMOD(aux);
	this->matriz[0][0] = aux;
}

Matriz::Matriz(int tamanioMatriz){
	this->tamanioMatriz = tamanioMatriz;

	for(int i = 0; i < this->tamanioMatriz; ++i){
		std::vector<int> columna;
		this->matriz.push_back(columna);
		for(int j = 0; j < this->tamanioMatriz; ++j){
			if(i != j)
				this->matriz[i].push_back(0);
			else
				this->matriz[i].push_back(1);
		}
	}
}

vector<unsigned int> Matriz::ObtenerVector(vector<unsigned int> vec) {
    int valor;
    int valorEnt;
    int lalala;
    unsigned int vectJ;
    int matrizIJ;
	vector<unsigned int> vecRes;
	for(int i =0; i < this->tamanioMatriz; i++){
		valor = 0;
		for(int j=0; j < this->tamanioMatriz; j++){
			vectJ = vec[j];
			matrizIJ = this->matriz[j][i];
			valor +=  vectJ* matrizIJ;
		}
		valorEnt = valor;
		lalala = valorEnt % 256;
		vecRes.push_back(lalala);
	}
	return vecRes;
}

vector<unsigned int> Matriz::ObtenerVectorAlVerres(vector<unsigned int> vec) {
	int valor;
	int valorEnt;
	int lalala;
	unsigned int vectJ;
	int matrizIJ;
	vector<unsigned int> vecRes;
	for(int i =0; i < this->tamanioMatriz; i++){
		valor = 0;
		for(int j=0; j < this->tamanioMatriz; j++){
			vectJ = vec[j];
			matrizIJ = this->matriz[i][j];
			valor +=  vectJ* matrizIJ;
		}
		valorEnt = valor;
		lalala = valorEnt % 256;
		vecRes.push_back(lalala);
	}
	return vecRes;
}

vector<unsigned char> Matriz::nuevoObtenerVector(vector<unsigned char> vec) {
    int valor;
    int valorEnt;
    int lalala;
    unsigned int vectJ;
    unsigned char charAux;
    int matrizIJ;
	vector<unsigned char> vecRes;
	unsigned int kk = 0;
	unsigned int zz = 0;
	while (kk < vec.size()) {
		for(int i =0; i < this->tamanioMatriz; i++){
			kk = zz*this->tamanioMatriz;
			valor = 0;
			for(int j=0; j < this->tamanioMatriz; j++){
				//cout << vec[kk];
				vectJ = (unsigned int)vec[kk];
				//cout << vectJ;
				matrizIJ = this->matriz[j][i];
				valor +=  vectJ* matrizIJ;
				kk++;
			}
			//cout << endl;
			//cout<< "\nLa variable kk vale: " << kk << endl;
			valorEnt = valor;
			lalala = valorEnt % 256;
			charAux = (unsigned char)lalala;
			//cout<< "\nLa variable charAux vale: " << charAux << endl;
			vecRes.push_back(charAux);
		}
		zz++;
	}
	return vecRes;
}

vector<unsigned char> Matriz::nuevoObtenerVectorAlVerres(vector<unsigned char> vec) {
	int valor;
	int valorEnt;
	int lalala;
	unsigned int vectJ;
	unsigned char charAux;
	int matrizIJ;
	vector<unsigned char> vecRes;
	unsigned int kk = 0;
	unsigned int zz = 0;
	while (kk < vec.size()) {
		for(int i =0; i < this->tamanioMatriz; i++){
			kk = zz*this->tamanioMatriz;
			valor = 0;
			for(int j=0; j < this->tamanioMatriz; j++){
				vectJ = (unsigned int) vec[kk];
				matrizIJ = this->matriz[i][j];
				valor +=  vectJ* matrizIJ;
				kk++;
			}
			//cout<< "\nLa variable kk vale: " << kk << endl;
			valorEnt = valor;
			lalala = valorEnt % 256;
			charAux = (unsigned char)lalala;
			vecRes.push_back(charAux);
		}
		zz++;
	}
	return vecRes;
}

long Matriz::InversaMOD(long num) {
	long res = 0;
	int i = 1;

	bool termino = false;
	while(!termino && i < 256) {
		res = (num * i);
		res = res % 256;

		if(res == 1)
			termino = true;
		else{
			i++;
		}
	}

	return i;
}

long Matriz::calcularDeterminante() {
	int resp = 1;
	long ij;
	for (int i = 0; i < tamanioMatriz; i++) {
		ij = matriz[i][i];
		resp = (resp * ij) % 256;
	}
	return resp;
}

vector<vector <int> > Matriz::calcularInversaLoca() {
	vector<vector <int> > inversa;
	long determinante = calcularDeterminante();
	determinante = InversaMOD(determinante);
	for (int kk = 0; kk < tamanioMatriz; kk++) {
		vector<int> vecAux;
		for (int p2 = 0; p2 < tamanioMatriz; p2++) {
			vector<vector <int> > subMatriz = crearSubMatriz(matriz, kk, p2);
			bool validar = validarSubMatriz(subMatriz);
			int adj = 0;
			if (validar) {
				adj = determinanteLoco(crearSubMatriz(matriz, kk, p2), tamanioMatriz -1);
			}
			adj = adj * determinante;
			int modAdj = adj;
			modAdj = pow(-1.0,kk+p2) * modAdj;
			vecAux.push_back(modAdj);
		}
		inversa.push_back(vecAux);
	}
	//Transpongo
	for (int i = 0; i < tamanioMatriz; i++) {
		for (int j = 0; j < tamanioMatriz; j++) {
			if (j != i) {
				int ij = inversa[i][j];
				int ji = inversa[j][i];
				inversa[i][j] = ji;
				inversa[j][i] = ij;
			}
		}
	}
	return inversa;
}

int Matriz::determinanteLoco(vector<vector <int> > matriz, int n) {
	int i,j,j1,j2;
	int det = 0;
	vector<vector <int> > m;
	for (int kk = 0; kk < n; kk++) {
		vector<int> vecAux;
		for (int p2 = 0; p2 < n; p2++) {
			vecAux.push_back(0);
		}
		m.push_back(vecAux);
	}

	if (n < 1) {

	} else if (n == 1) {
		det = matriz[0][0];
	} else if (n == 2) {
		det = matriz[0][0] * matriz[1][1] - matriz[1][0] * matriz[0][1];
	} else {
		det = 0;
		for (j1=0;j1<n;j1++) {
			for (i=1;i<n;i++) {
				j2 = 0;
				for (j=0;j<n;j++) {
					if (j == j1) {
						continue;
					}
					m[i-1][j2] = matriz[j][i];
					j2++;
				}
			}

			// matriz[0][0] != 0 && matriz[1][0] != 0
			// matriz[1][1] != 0 && matriz[1][0] != 0
			// matriz[0][0] != 0 && matriz[0][1] != 0
			// matriz[1][1] != 0 && matriz[0][1] != 0
			if (matriz[j1][0] != 0) {
				det += pow(-1.0,1.0+j1+1.0) * matriz[j1][0] * determinanteLoco(m, n-1);
				long detLong = det;
				det = detLong % 256;
			}
		}
	}
	return(det);
}

vector<vector <int> > Matriz::crearSubMatriz(vector<vector <int> > matriz, int i, int j) {
	vector<vector <int> > m;
	for (int kk = 0; kk < tamanioMatriz; kk++) {
		if (i != kk) {
			vector<int> vecAux;
			for (int p2 = 0; p2 < tamanioMatriz; p2++) {
				if (p2 != j) {
					vecAux.push_back(matriz[kk][p2]);
				}
			}
			m.push_back(vecAux);
		}
	}
	return m;
}

bool Matriz::validarSubMatriz(vector<vector <int> > matriz) {
	for (unsigned int i = 0; i < matriz.size(); i++) {
		if (matriz[0][i] != 0) {
			return true;
		}
	}
	return false;
}

void Matriz::ImprimirMatriz(){
	for(int i=0; i< tamanioMatriz; i++){
		for(int j=0; j< tamanioMatriz-1; j++) {
			cout << this->matriz[i][j] << " ";
		}
		cout << this->matriz[i][tamanioMatriz-1] << endl;
	}
}

Matriz::~Matriz() {
	for (unsigned int i = 0; i < matriz.size(); i++) {
		this->matriz[i].clear();

	}
}
